﻿//////////////////////////////////////////////////////////////////////////////
//
//  Copyright © 1998-2024 Glodon Company Limited.  All rights reserved.
//
//  Use of this software is subject to the terms of the Glodon license
//  agreement provided at the time of installation or download, or which
//  otherwise accompanies this software in either electronic or hard copy form.  
//
//////////////////////////////////////////////////////////////////////////////

// Reviewed

#pragma once

#include <sstream>

#include "GcmpApiDeclaration.h"
#include "Vector2d.h"


namespace gcmp
{
    /// \brief  二维包围盒
    ///
    /// 
    class GCMP_MATH_INTERFACE_EXPORT Box2d
    {
    public:
        DEFINE_VISUAL_TYPE_DOUBLE()

    public:

        /// \brief  创建一个空包围盒
        ///
        /// \return 空包围盒
        static Box2d CreateEmptyBox2d();

        /// \brief   通过最小点， 最大点创建Box2d
        ///
        /// \param min  最小点
        /// \param max  最大点
        /// \return 
        static Box2d Create(const Vector2d& min, const Vector2d& max);

        /// \brief  获得包围盒的最小点
        ///
        /// \return 最小点
        const Vector2d& GetMinPoint() const;

        /// \brief  获得包围盒的最大点
        ///
        /// \return 最大点
        const Vector2d& GetMaxPoint() const;

        /// \brief   平移包围盒
        ///
        /// \param moveVector  移动向量
        /// \return  true: 成功， false: 失败
        bool Translate(const Vector2d& moveVector);

        /// \brief    设置包围盒的最小点和最大点
        ///
        /// \param min  最小点
        /// \param max  最大点
        /// \return true: 设置成功， false: 设置失败
        bool Set(const Vector2d& min, const Vector2d& max);

        /// \brief   判断包围盒是否为空
        ///
        /// \return  true: 空，false: 非空
        bool IsEmpty() const;

        /// \brief   获得包围盒的中心点
        ///
        /// \return  中心点
        Vector2d GetCenter() const;
      
        /// \brief  当前包围盒扩大使其包含传入点
        ///
        /// \param point 传入点
        /// \return  true: 成功， false: 失败
        bool MergePoint(const Vector2d & point);

        /// \brief   当前包围盒和传入包围盒合并，并设置当前包围盒为合并的结果
        ///
        /// \param box  传入包围盒
        /// \return true: 成功， false: 失败
        bool MergeBox(const Box2d& box);

        /// \brief   在给定误差内，判断当前包围盒是否包含传入点（在包围盒内或在包围盒上）
        ///
        /// \param point      传入点
        /// \param tolerance  给定误差
        /// \return  true: 包含， false: 不包含
        bool Contain(const Vector2d & point, double tolerance = Constants::DOUBLE_EPS) const;

        /// \brief     在给定误差内，判断传入包围盒是否和当前包围盒相交
        ///
        /// \param box 传入包围盒
        /// \param tolerance   给定误差
        /// \return true: 相交 ，false: 不相交
        bool IsIntersect(const Box2d& box, double tolerance = Constants::DOUBLE_EPS) const;

    public:

        /// \brief 缺省构造函数:构造出一个无效的二维包围盒
        ///
        /// \return 
        Box2d();
        
        /// \brief   用一个二维点构造大小为零的包围盒: 构造的包围盒两个角点均为输入的向量值
        ///
        /// \param pt  二维点
        /// 
        explicit Box2d(const Vector2d& pt);

        /// \brief   用最小点和最大点构造包围盒
        ///
        /// \param min  最小点 
        /// \param max 最大点
        /// 
        Box2d(const Vector2d& min, const Vector2d& max);

        /// \brief     通过包围盒的最小点的坐标和最大点坐标构造包围盒
        ///
        /// \param minX   最小点X坐标
        /// \param minY   最小点Y坐标
        /// \param maxX   最大点X坐标
        /// \param maxY   最大点Y坐标
        /// 
        Box2d(const double& minX, const double& minY, const double& maxX, const double& maxY);

        /// \brief       通过包围盒中心点的坐标和X与Y方向上的范围构造包围盒
        ///
        /// \param centerPos   包围盒中心点
        /// \param extentX     X方向上的范围
        /// \param extentY     Y方向上的范围
        /// \return 
        Box2d(const Vector2d& centerPos, const double& extentX, const double& extentY);

        /// \brief   拷贝构造包围盒
        ///
        /// \param box  传入的包围盒
        ///
        Box2d(const Box2d& box);

        /// \brief     设置包围盒的最小点坐标和最大点坐标
        ///
        /// \param minx   最小点X坐标
        /// \param miny   最小点Y坐标
        /// \param maxx   最大点X坐标
        /// \param maxy   最大点Y坐标
        /// \return 
        bool Set(const double minX, const double minY, const double maxX, const double maxY);

        /// \brief   获得包围盒的宽度和高度
        ///
        /// \return  X坐标为包围盒的宽度，Y坐标为包围盒的高度
        Vector2d GetSize() const;

        /// \brief     获得包围盒的4个角点
        ///
        /// \param corner   返回角点数组
        /// 
        void GetCorners(Vector2d corner[4]) const;

        /// \brief       在给定误差下，包围盒是否在传入包围内，包括内相切
        ///
        /// \param box   传入包围盒
        /// \param tolerance  给定误差
        /// \return   true:  当前包围盒在传入包围盒内，false:  当前包围盒不在传入包围盒内
        bool In(const Box2d& box, double tolerance = Constants::DOUBLE_EPS) const;

        /// \brief   初始化包围盒
        ///
        /// \return true  成功;  false  失败
        bool MakeEmpty();

        /// \brief   缩放外包盒，以(0, 0)为中心进行缩放
        ///
        /// \param scaleX  X方向缩放系数
        /// \param scaleY  Y方向缩放系数
        /// \return true: 成功， false: 失败
        bool ScaleBasedOnOriginPoint(double scaleX, double scaleY);

        /// \brief    缩放外包盒，以盒的中心为中心进行缩放
        ///
        /// \param scaleX  X方向缩放系数
        /// \param scaleY  Y方向缩放系数
        /// \return  true: 成功， false: 失败
        GCMP_OBSOLETE_API(2023 / 10 / 08, Box2d::ScaleBasedOnBoxCenterPoint(double, double))
        bool ScaleBasedOnCenterPoint(double scaleX, double scaleY);

        /// \brief   缩放包围盒，以包围盒中心点为中心按比例缩放
        ///          scale在[0,1)表示缩小，1表示不变，大于1表示放大
        /// \param scaleX  X方向缩放比例
        /// \param scaleY  Y方向缩放比例
        /// \return  true：成功，false：失败
        bool ScaleBasedOnBoxCenterPoint(double scaleX, double scaleY);

        /// \brief      获得当前包围盒和传入包围盒的交集
        ///
        /// \param Box  传入包围盒
        /// \param IntersectBox  交集
        /// \param tolerance  给定误差
        /// \return  true：存在相交包围盒，false: 不相交
        bool GetIntersectBox(const Box2d& Box, Box2d& IntersectBox, double tolerance = Constants::DOUBLE_EPS);

        /// \brief   包围盒对象按照规定格式字符串输出
        ///
        /// \return  字符串
        std::wstring GetStringForDebug() const;

        /// \brief    根据字符串生成包围盒
        ///
        /// \param str  传入的字符串
        /// \return  生成的包围盒
        static OwnerPtr<Box2d> LoadFromString(const std::wstring& str);


        /// \brief   赋值操作符
        ///
        /// \param box 传入的包围盒
        /// \return 当前包围盒
        Box2d & operator= (const Box2d & box);
        
        /// \brief   -=操作符，用于包围盒平移
        ///
        /// \param   v   移动的向量
        /// \return  当前包围盒
        Box2d& operator -= (const Vector2d & v);

        /// \brief   += 操作符，用于包围盒平移
        ///
        /// \param   v   移动的向量
        /// \return  当前包围盒
        Box2d& operator += (const Vector2d & v);

        /// \brief  *= 操作符，用于包围盒缩放
        ///
        /// \param   scale   缩放系数
        /// \return  当前包围盒
        Box2d & operator *= (double scale);

    private:
        Vector2d m_min;
        Vector2d m_max;

    public:
        static const Box2d Zero;
    };

    /// \brief   逻辑等号操作符，判断两个包围盒的是否相等
    ///
    /// \param   b1 传入的包围盒1
    /// \param   b2 传入的包围盒2
    /// \return   true: 相等，false:  不相等
    GCMP_MATH_INTERFACE_EXPORT bool operator ==(const Box2d& b1, const Box2d& b2);


    /// \brief   逻辑不等号操作符，判断两个包围盒是否不相等
    ///
    /// \param   b1 传入的包围盒1
    /// \param   b2 传入的包围盒2
    /// \return   true: 不相等，false:  相等
    GCMP_MATH_INTERFACE_EXPORT bool operator !=(const Box2d& b1, const Box2d& b2);

};

